#include "window.h"
#include "upgrade.h"
#include "product.h"
#include "settings.h"
#include "state.h"
#include "finder.h"
#include "autobuy.h"
#include "power_orb.h"

#include <QGridLayout>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QTabWidget>
#include <QTimer>
#include <QToolButton>

MainWindow::MainWindow( QWidget * parent ) {
    setParent(parent);
    grid = new QGridLayout{this};
    grid->setSizeConstraint(QLayout::SetFixedSize);
    playerData = std::make_shared<State>();

    // buildings
    addUpgrade( UpgradeType::SolarPanel, 0, 0 );
    addUpgrade( UpgradeType::WindMill, 0, 1 );
    addUpgrade( UpgradeType::Reflector, 0, 2 );
    // generators
    addUpgrade( UpgradeType::Generator_Power, 1, 0 );
    addUpgrade( UpgradeType::Generator_XP, 1, 1 );
    addUpgrade( UpgradeType::Generator_Asset, 1, 2 );
    // asset cost reduction
    addUpgrade( UpgradeType::Cost_Reduction_Assets, 1, 3 );
    // building efficieny
    addUpgrade( UpgradeType::Effeciency_SolarPanel, 2, 0 );
    addUpgrade( UpgradeType::Effeciency_WindWill, 2, 1 );
    addUpgrade( UpgradeType::Effeciency_Reflector, 2, 2 );
    // orb cost reduction
    addUpgrade( UpgradeType::Cost_Reduction_Orb, 2, 3 );
    // settings and finder tabs
    qRegisterMetaType<OptimumValues>();
    Finder * finder = new Finder( playerData );
    connect( finder, &Finder::optimumFound, this, &MainWindow::setOptimum );
    connect( this, &MainWindow::updateData, finder, &Finder::updateData );
    connect( finder->findChild<QToolButton *>( "ModeButton" ), &QToolButton::toggled, this, &MainWindow::setAutoControlMode );

    Settings * settings = new Settings{ this };
    connect( settings, &Settings::tierChanged, this, &MainWindow::setTier );
    connect( settings, &Settings::playthroughChanged, this, &MainWindow::setPlaythrough );

    AutoBuyWidget * autobuy = new AutoBuyWidget{ playerData, this };
    connect( autobuy, &AutoBuyWidget::stepChanged, this, &MainWindow::setStep );
    connect( this, &MainWindow::updateData, autobuy, &AutoBuyWidget::updateData );

    PowerOrbWidget * powerOrb = new PowerOrbWidget{ this };
    connect( powerOrb->findChild<QSpinBox *>(), QOverload<int>::of( &QSpinBox::valueChanged ), this, &MainWindow::claimOrb );
    connect( this, &MainWindow::updateData, powerOrb, &PowerOrbWidget::updateData );

    QTabWidget * tabs = new QTabWidget{ this };
    tabs->addTab( settings, QIcon::fromTheme( "configure" ), "" );
    tabs->addTab( finder, QIcon::fromTheme( "search" ), "" );
    tabs->addTab( autobuy, QIcon{ ":/product/Asset" }, "" );
    tabs->addTab( powerOrb, QIcon( ":/upgrade/Orb cost reduction" ), "" );
    tabs->setFixedWidth(180);
    // clear focus on tab change if focus inside tab widget
    connect( tabs, &QTabWidget::currentChanged, tabs, [tabs]() {
        if ( tabs->currentWidget()->focusWidget() )
        { tabs->currentWidget()->focusWidget()->clearFocus(); }
    } );
    grid->addWidget(tabs, 0, 3);

    //  products
    addProduct(ProductType::Power, 0, 4);
    addProduct(ProductType::XP, 1, 4);
    addProduct(ProductType::Asset, 2, 4);

    //  tick timer
    QTimer * tickTimer = new QTimer{this};
    connect(tickTimer, &QTimer::timeout, this, [this, tickTimer]() { tick(tickTimer->interval()/1000 ); } );
    tickTimer->setInterval(1000);
    tickTimer->start();
}

/*********
 * SLOTS *
 *********/

void MainWindow::setLevel( UpgradeType type, int level ) {
    // upgrade item
    playerData->upgrade(type,  level,  true);
    updateViews();
}

void MainWindow::setAutoControlMode( bool mode ) {
    playerData->setAutoControl(mode);
    updateViews();
}

void MainWindow::setStep( int step ) {
    playerData->setStep(step);
    updateViews();
}

void MainWindow::setTier( double tier ) {
    playerData->setTier(1 + tier * 0.1);
    updateViews();
}

void MainWindow::setPlaythrough( int playthrough ) {
    playerData->setPlaythrough(playthrough);
    updateViews();
}

void MainWindow::setProductWeight( ProductType type, double weight ) {
    playerData->setProductWeight( type, weight );
    updateViews();
}

void MainWindow::tick( int seconds ) {
    playerData->tick(std::chrono::seconds{seconds});
    updateViews();
}

void MainWindow::setProductValue( ProductType type, double value ) {
    playerData->setProductValue(type, value);
    updateViews();
}

void MainWindow::setProductFactor( ProductType type, double factor ) {
    playerData->setProductFactor(type,  factor);
    updateViews();
}

void MainWindow::setOptimum( const OptimumValues & optimum ) {
    playerData->setProductFactor(ProductType::Power, optimum.powerFactor);
    playerData->setProductFactor(ProductType::XP, optimum.reduction);
    playerData->setProductFactor(ProductType::Asset, optimum.assetFactor);
    playerData->setProductWeight(ProductType::Power, optimum.powerWeight);
    playerData->setProductWeight(ProductType::XP, optimum.xpWeight);
    playerData->setProductWeight(ProductType::Asset, optimum.assetWeight);
    updateViews();
}

void MainWindow::claimOrb( int orbs ) {
    playerData->claimOrbs(orbs);
    updateViews();
}

/***********
 * PRIVATE *
 ***********/

void MainWindow::updateViews() {
    emit updateData(*playerData);
}

UpgradeView * MainWindow::addUpgrade( UpgradeType type, int row, int column ) {
    UpgradeView * view =  new UpgradeView(type, *playerData, this);
    grid->addWidget( view, row, column );
    connect( view, &UpgradeView::levelChanged, this, &MainWindow::setLevel );
    connect( this, &MainWindow::updateData, view, &UpgradeView::updateData );
    return view;;
}

ProductView * MainWindow::addProduct( ProductType type, int row, int column ) {
    ProductView * view = new ProductView(type, *playerData, this);
    grid->addWidget(view, row, column);
    connect( view, &ProductView::weightChanged, this, &MainWindow::setProductWeight );
    connect( view, &ProductView::valueChanged, this, &MainWindow::setProductValue );
    connect( view, &ProductView::factorChanged, this, &MainWindow::setProductFactor );
    connect(this, &MainWindow::updateData, view, &ProductView::updateData);
    return view;
}
